import hashlib
import os, sys
import gzip

import sqlalchemy
from sqlalchemy import Table, Column, Integer, String, MetaData, ForeignKey
from sqlalchemy.orm import mapper
from sqlalchemy.orm import sessionmaker
from sqlalchemy.orm.exc import NoResultFound


class BackupBlock(object):
        def __init__(self, offset, number, hash, filename):
		self.offset = offset
		self.number = number
		self.hash = hash
		self.filename = filename

class StorageHash:
	block_size=4096000

	root_storage_path = "" 
	manifest_storage_path = "" 
	block_storage_path = "" 
	block_hash_directory_depth = 10
	db = ""

	def set_storage_path(self, path):
		self.root_storage_path = path
		self.manifest_storage_path = self.root_storage_path + 'manifest'
		self.block_storage_path = self.root_storage_path + 'block'

	def manifest_filename(self, name):
		return self.manifest_storage_path + '/' + name + '.db'

	def block_hash_storage_path(self, block_hash):
		for i in range(0,self.block_hash_directory_depth):
			block_hash = block_hash[:(i*2+1)] + '/' + block_hash[(i*2+1):]
		return self.block_storage_path + '/' + block_hash[:(i*2+1)]

	def block_filename(self, block_hash):
		return self.block_hash_storage_path(block_hash) + '/'  +  str(block_hash) +".block"

	def create_backup(self, name):
		engine = sqlalchemy.create_engine('sqlite:///'+self.manifest_filename(name), echo=False)
		metadata = sqlalchemy.MetaData()
		block_table = Table('block',metadata,
	        Column('id', Integer, primary_key=True),
		Column('offset', Integer),
		Column('number', Integer),
		Column('hash', String),
		Column('filename', String)
		)
		metadata.create_all(engine)
		mapper(BackupBlock, block_table)
		Session = sessionmaker(bind=engine)
		self.db = Session()

	def hash(self, block):
		m = hashlib.sha256()
		m.update(block)
		return m.hexdigest()

	def need_for_block(self, offset, hash1):
		if self.db.query(BackupBlock).filter(BackupBlock.hash==hash1).count()>0:
			return False
		else:
			return True

	def store_block(self, offset, block):
		block_nr = int(offset / self.block_size)
		block_hash = self.hash(block)


		if self.db.query(BackupBlock).filter(BackupBlock.hash==block_hash).count()==0:
			try:
				os.makedirs(self.block_hash_storage_path(block_hash))
			except:
				True

			try:	
				mode = os.stat(self.block_filename(block_hash)).st_mode
			except:
				with gzip.GzipFile(self.block_filename(block_hash), "wb") as fb:
					fb.write(block)

		self.db.add(BackupBlock(offset, block_nr, block_hash, self.block_filename(block_hash)) )
		self.db.commit()
